﻿#include "NameServer.h"

bool register_name(char *inbuf,char *outbuf,char *name,struct in_addr *ip)
{
  unsigned char nb_flags = 0;
  int count;
  static uint16 name_trn_id = 0x4242;
  char *p;

  Debug(1,"Registering name %s (%s) nb_flags=0x%x\n",
	name, inet_ntoa(*ip) ,nb_flags);
  //不如定义一个结构体，使用编译器的packed属性，但要注意
  //字节对齐的问题。
  SSVAL(outbuf,0,name_trn_id++);
  CVAL(outbuf,2) = (0x5<<3) | 0x1;
  CVAL(outbuf,3) = (1<<4) | 0x0;
  SSVAL(outbuf,4,1);
  SSVAL(outbuf,6,0);
  SSVAL(outbuf,8,0);
  SSVAL(outbuf,10,1);  
  p = outbuf+12;
  name_mangle(name,p);
  p += name_len(p);
  SSVAL(p,0,0x20);
  SSVAL(p,2,0x01);

  count = 3;
  while (count--)
  {
      Debug(2,"Sending query request for %s at (%s)\n",name,inet_ntoa(myip));

      show_nmb(outbuf);
      if (!send_packet(outbuf,nmb_len(outbuf),&bcast_ip,137,SOCK_DGRAM))
	        return false;
      if (receive_nmb(inbuf,3))
	  {
          int rec_name_trn_id = SVAL(inbuf,0);
          int opcode = CVAL(inbuf,2) >> 3;
          int nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
          int rcode = CVAL(inbuf,3) & 0xF;

          /* is it a positive response to our request? */
          if ((rec_name_trn_id = name_trn_id) && 
                  (opcode == 0 && nm_flags == 0x58 && rcode != 0 && rcode != 7))
            {
              char qname[100];
              name_extract(inbuf,12,qname);
              if (name_equal(qname,name))
             {
              Debug(0,"Someone (%s) gave us a negative name regregistration response!\n",
                inet_ntoa(lastip));
              return false;
             }
	        }	  
	  
          /* it's something else - process it anyway, unless we are running
           as a daemon. This is necessary as we may have been started by a 
           name query of our name arriving on port 137 (often happens) */
          if (!daemon)
            {
              show_nmb(inbuf);
              construct_reply(inbuf,outbuf + nmb_len(outbuf));
            }
	  }
  }

  /* no negative replies, send a demand */
  p = outbuf;
  SSVAL(outbuf,0,name_trn_id++);
  CVAL(outbuf,2) = (0x5<<3);
  CVAL(outbuf,3) = (1<<4) | 0x0;
  SSVAL(outbuf,4,1);
  SSVAL(outbuf,6,0);
  SSVAL(outbuf,8,0);
  SSVAL(outbuf,10,1);  
  p = outbuf+12;
  name_mangle(name,p);
  p += name_len(p);
  SSVAL(p,0,0x20);
  SSVAL(p,2,0x1);
  p += 4;
  CVAL(p,0) = 0xC0;
  CVAL(p,1) = 12;
  p += 2;
  SSVAL(p,0,0x20);
  SSVAL(p,2,0x1);
  SIVAL(p,4,myttl);
  SSVAL(p,8,6);
  CVAL(p,10) = nb_flags;
  CVAL(p,11) = 0;
  p += 12;
  memcpy(p,ip,4);
  p += 4;
  
  Debug(2,"Sending reg demand for %s at (%s)\n",name,inet_ntoa(*ip));

  show_nmb(outbuf);
  return(send_packet(outbuf,nmb_len(outbuf),&bcast_ip,137,SOCK_DGRAM));
}


/****************************************************************************
  construct a reply to the incoming packet
****************************************************************************/
void construct_reply(char *inbuf,char *outbuf)
{
  int opcode = CVAL(inbuf,2) >> 3;
  int nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
  int rcode = CVAL(inbuf,3) & 0xF;
  
  if (opcode == 0x5 && (nm_flags & ~1) == 0x10 && rcode == 0)
    reply_reg_request(inbuf,outbuf);

  if (opcode == 0 && (nm_flags&~1) == 0x10 && rcode == 0)
    reply_name_query(inbuf,outbuf);
}


/****************************************************************************
reply to a reg request
****************************************************************************/
void reply_reg_request(char *inbuf,char *outbuf)
{
  int name_trn_id = SVAL(inbuf,0);
  char qname[100]="";
  int ttl;
  char *p = inbuf;
  struct in_addr ip;
  int n=0;
  unsigned char nb_flags;
  
  name_extract(inbuf,12,qname);

  p += 12;
  p += name_len(p);
  p += 4;
  p += name_len(p);
  p += 4;
  ttl = IVAL(p,0);
  nb_flags = CVAL(p,6);
  p += 8;
  memcpy(&ip,p,4);

  Debug(2,"Name registration request for %s (%s) nb_flags=0x%x\n",
	qname,inet_ntoa(ip),nb_flags);

  /* if the name doesn't exist yet then don't respond */
  if ((n = find_name(qname)) < 0)
    return;

  /* if it's a group name and being registered as a group then it's OK */
  if ((names[n].nb_flags & 0x80) && (nb_flags & 0x80))
    return;

  /* if it's not my name then don't worry about it */
  if (!name_equal(myname,qname)) //why??
    return;

  Debug(0,"Someones using my name (%s), sending negative reply\n",qname);

  /* Send a NEGATIVE REGISTRATION RESPONSE to protect our name */
  SSVAL(outbuf,0,name_trn_id);
  CVAL(outbuf,2) = (1<<7) | (0x5<<3) | 0x5;
  CVAL(outbuf,3) = (1<<7) | 0x6;
  SSVAL(outbuf,4,0);
  SSVAL(outbuf,6,1);
  SSVAL(outbuf,8,0);
  SSVAL(outbuf,10,0);  
  p = outbuf+12;
  strcpy(p,inbuf+12);  //right??
  p += name_len(p);
  SSVAL(p,0,0x20);
  SSVAL(p,2,0x1);
  SIVAL(p,4,names[n].ttl);
  SSVAL(p,8,6);
  CVAL(p,10) = nb_flags; //why??
  CVAL(p,11) = 0;
  p += 12;

  //Why??, that should be myip
  memcpy(p,&ip,4);      /* IP address of the name's owner (that's us) */
  p += 4;

  if (ip_equal(&ip,&bcast_ip))
    {
      Debug(0,"Not replying to broadcast address\n");
      return;
    }

  show_nmb(outbuf);
  send_packet(outbuf,nmb_len(outbuf),&ip,137,SOCK_DGRAM);

  return;
}

bool query_name(char *inbuf,char *outbuf,char *name, struct in_addr *ip)
{
  unsigned char nb_flags = 0;
  int count;
  int wait_time = 5;
  static uint16 name_trn_id = 0x4242;
  char *p;

  Debug(1,"Querying name %s nb_flags=0x%x\n",
	name, nb_flags);
  //不如定义一个结构体，使用编译器的packed属性，但要注意
  //字节对齐的问题。
  SSVAL(outbuf,0,name_trn_id++);
  CVAL(outbuf,2) = 0x01;
  CVAL(outbuf,3) = 0x10;
  SSVAL(outbuf,4,1);
  SSVAL(outbuf,6,0);
  SSVAL(outbuf,8,0);
  SSVAL(outbuf,10,0);  
  p = outbuf+12;
  name_mangle((unsigned char*)name,(unsigned char*)p);
  p += name_len(p);
  SSVAL(p,0,0x20);
  SSVAL(p,2,0x1);
  p += 4;

  count = 3;
  while (count--)
    {
      Debug(2,"Sending query request for %s at (%s)\n",name,inet_ntoa(myip));


      show_nmb(outbuf);
      if (!send_packet(outbuf,nmb_len(outbuf),&bcast_ip,137,SOCK_DGRAM))
	        return false;
      if (count == 1)
          wait_time = 20;
      if (receive_nmb(inbuf, wait_time))
	{
      int rec_name_trn_id = SVAL(inbuf,0);
	  int opcode = CVAL(inbuf,2) >> 3;
	  int nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
	  int rcode = CVAL(inbuf,3) & 0xF;

	  /* is it a negative response to our request? */
	  if ((rec_name_trn_id = name_trn_id) && 
              (opcode == 0 && (nm_flags&~0x28) == 0x50 && rcode == 0))
	    {
	      char qname[100];
	      name_extract(inbuf,12,qname);
	      if (name_equal(qname,name))
		 {
		  Debug(0,"Someone (%s) gave us a positive name query response!\n",
			inet_ntoa(lastip));
          char *ptr = inbuf + 12;
          int  namelen = name_len(ptr);
          ptr += (namelen + 12);
          memcpy(ip, ptr, sizeof(*ip));
		  return true;
		 }
	    }	  
      else if ((rec_name_trn_id = name_trn_id) && 
               (opcode == 0 && (nm_flags&~0x08) == 0x50 && rcode != 0))
      {

        return false;
      }
	  
	  /* it's something else - process it anyway, unless we are running
	   as a daemon. This is necessary as we may have been started by a 
	   name query of our name arriving on port 137 (often happens) */
	  if (!daemon)
	    {
	      show_nmb(inbuf);
	      construct_reply(inbuf,outbuf + nmb_len(outbuf));
	    }
	}
    }
    return false;
}
/****************************************************************************
reply to a name query
****************************************************************************/
void reply_name_query(char *inbuf,char *outbuf)
{
  int name_trn_id = SVAL(inbuf,0);
  char qname[100]="";
  char *p = inbuf;
  unsigned char nb_flags = 0;
  struct in_addr tmpip;

  name_extract(inbuf,12,qname);

  /* if it's not my name then return */
  if (!name_equal(qname,myname))
    return;

  Debug(2,"(%s) querying my name (%s), sending positive reply\n",
	inet_ntoa(lastip), qname);
 
  /* Send a POSITIVE NAME QUERY RESPONSE to ack our presence */
  SSVAL(outbuf,0,name_trn_id);
  CVAL(outbuf,2) = (1<<7) | 0x5;
  CVAL(outbuf,3) = 0;
  SSVAL(outbuf,4,0);
  SSVAL(outbuf,6,1);
  SSVAL(outbuf,8,0);
  SSVAL(outbuf,10,0);  
  p = outbuf+12;
  strcpy(p,inbuf+12);
  p += name_len(p);
  SSVAL(p,0,0x20);
  SSVAL(p,2,0x1);
  SIVAL(p,4,myttl);
  SSVAL(p,8,6);
  CVAL(p,10) = nb_flags;
  CVAL(p,11) = 0;
  p += 12;
  memcpy(p,&myip,4);
  p += 4;

  show_nmb(outbuf);

  tmpip = lastip;
  send_packet(outbuf,nmb_len(outbuf),&tmpip,137,SOCK_DGRAM);

  return;
}
